home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / php / pear / Net / DNS / Packet.php < prev    next >
PHP Script  |  2004-03-24  |  19KB  |  596 lines

  1. <?php
  2. /*
  3.  *  License Information:
  4.  *
  5.  *    Net_DNS:  A resolver library for PHP
  6.  *    Copyright (C) 2002 Eric Kilfoil eric@ypass.net
  7.  *
  8.  *    This library is free software; you can redistribute it and/or
  9.  *    modify it under the terms of the GNU Lesser General Public
  10.  *    License as published by the Free Software Foundation; either
  11.  *    version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  *    This library is distributed in the hope that it will be useful,
  14.  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  *    Lesser General Public License for more details.
  17.  *
  18.  *    You should have received a copy of the GNU Lesser General Public
  19.  *    License along with this library; if not, write to the Free Software
  20.  *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  21.  */
  22.  
  23. /* Net_DNS_Packet object definition {{{ */
  24. /**
  25.  * A object represation of a DNS packet (RFC1035)
  26.  *
  27.  * This object is used to manage a DNS packet.  It contains methods for
  28.  * DNS packet compression as defined in RFC1035, as well as parsing  a DNS
  29.  * packet response from a DNS server, or building a DNS packet from  the
  30.  * instance variables contained in the class.
  31.  *
  32.  * @package Net_DNS
  33.  */
  34. class Net_DNS_Packet
  35. {
  36.     /* class variable definitions {{{ */
  37.     /**
  38.      * debugging flag
  39.      *
  40.      * If set to TRUE (non-zero), debugging code will be displayed as the
  41.      * packet is parsed.
  42.      *
  43.      * @var boolean $debug
  44.      * @access  public
  45.      */
  46.     var $debug;
  47.     /**
  48.      * A packet Header object.
  49.      *
  50.      * An object of type Net_DNS_Header which contains the header
  51.      * information  of the packet.
  52.      *
  53.      * @var object Net_DNS_Header $header
  54.      * @access  public
  55.      */
  56.     var $header;
  57.     /**
  58.      * A hash of compressed labels
  59.      *
  60.      * A list of all labels which have been compressed in the DNS packet
  61.      * and  the location offset of the label within the packet.
  62.      *
  63.      * @var array   $compnames
  64.      */
  65.     var $compnames;
  66.     /**
  67.      * The origin of the packet, if the packet is a server response.
  68.      *
  69.      * This contains a string containing the IP address of the name server
  70.      * from which the answer was given.
  71.      *
  72.      * @var string  $answerfrom
  73.      * @access  public
  74.      */
  75.     var $answerfrom;
  76.     /**
  77.      * The size of the answer packet, if the packet is a server response.
  78.      *
  79.      * This contains a integer containing the size of the DNS packet the
  80.      * server responded with if this packet was received by a DNS server
  81.      * using the query() method.
  82.      *
  83.      * @var string  $answersize
  84.      * @access  public
  85.      */
  86.     var $answersize;
  87.     /**
  88.      * An array of Net_DNS_Question objects
  89.      *
  90.      * Contains all of the questions within the packet.  Each question is
  91.      * stored as an object of type Net_DNS_Question.
  92.      *
  93.      * @var array   $question
  94.      * @access  public
  95.      */
  96.     var $question;
  97.  
  98.     /* }}} */
  99.     /* class constructor - Net_DNS_Packet($debug = FALSE) {{{ */
  100.     /*
  101.      * unfortunately (or fortunately), we can't follow the same
  102.      * silly method for determining if name is a hostname or a packet
  103.      * stream in PHP, since there is no ref() function.  So we're going
  104.      * to define a new method called parse to deal with this
  105.      * circumstance and another method called buildQuestion to build a question.
  106.      * I like it better that way anyway.
  107.      */
  108.     /**
  109.      * Initalizes a Net_DNS_Packet object
  110.      *
  111.      * @param boolean $debug Turns debugging on or off
  112.      */
  113.     function Net_DNS_Packet($debug = FALSE)
  114.     {
  115.         $this->debug = $debug;
  116.         $this->compnames = array();
  117.     }
  118.  
  119.     /* }}} */
  120.     /* Net_DNS_Packet::buildQuestion($name, $type = "A", $class = "IN") {{{ */
  121.     /**
  122.      * Adds a DNS question to the DNS packet
  123.      *
  124.      * @param   string $name    The name of the record to query
  125.      * @param   string $type    The type of record to query
  126.      * @param   string $class   The class of record to query
  127.      * @see Net_DNS::typesbyname(), Net_DNS::classesbyname()
  128.      */
  129.     function buildQuestion($name, $type = "A", $class = "IN")
  130.     {
  131.         $this->header = new Net_DNS_Header();
  132.         $this->header->qdcount = 1;
  133.         $this->question[0] = new Net_DNS_Question($name, $type, $class);
  134.         $this->answer = NULL;
  135.         $this->authority = NULL;
  136.         $this->additional = NULL;
  137.         if ($this->debug) {
  138.             $this->display();
  139.         }
  140.     }
  141.  
  142.     /* }}} */
  143.     /* Net_DNS_Packet::parse($data) {{{ */
  144.     /**
  145.      * Parses a DNS packet returned by a DNS server
  146.      *
  147.      * Parses a complete DNS packet and builds an object hierarchy
  148.      * containing all of the parts of the packet:
  149.      * <ul>
  150.      *   <li>HEADER   
  151.      *   <li>QUESTION   
  152.      *   <li>ANSWER || PREREQUISITE   
  153.      *   <li>ADDITIONAL || UPDATE   
  154.      *   <li>AUTHORITY
  155.      * </ul>
  156.      * 
  157.      * @param string $data  A binary string containing a DNS packet
  158.      * @return boolean TRUE on success, NULL on parser error
  159.      */
  160.     function parse($data)
  161.     {
  162.         if ($this->debug) {
  163.             echo ";; HEADER SECTION\n";
  164.         }
  165.  
  166.         $this->header = new Net_DNS_Header($data);
  167.  
  168.         if ($this->debug) {
  169.             $this->header->display();
  170.         }
  171.  
  172.         /*
  173.          *  Print and parse the QUESTION section of the packet
  174.          */
  175.         if ($this->debug) {
  176.             echo "\n";
  177.             $section = ($this->header->opcode  == "UPDATE") ? "ZONE" : "QUESTION";
  178.             echo ";; $section SECTION (" . $this->header->qdcount . " record" .
  179.                 ($this->header->qdcount == 1 ? "" : "s") . ")\n";
  180.         }
  181.  
  182.         $offset = 12;
  183.  
  184.         $this->question = array();
  185.         for ($ctr = 0; $ctr < $this->header->qdcount; $ctr++) {
  186.             list($qobj, $offset) = $this->parse_question($data, $offset);
  187.             if (is_null($qobj)) {
  188.                 return(NULL);
  189.             }
  190.  
  191.             $this->question[count($this->question)] = $qobj;
  192.             if ($this->debug) {
  193.                 echo ";;\n;";
  194.                 $qobj->display();
  195.             }
  196.         }
  197.  
  198.         /*
  199.          *  Print and parse the PREREQUISITE or ANSWER  section of the packet
  200.          */
  201.         if ($this->debug) {
  202.             echo "\n";
  203.             $section = ($this->header->opcode == "UPDATE") ? "PREREQUISITE" :"ANSWER";
  204.             echo ";; $section SECTION (" .
  205.                 $this->header->ancount . " record" .
  206.                 (($this->header->ancount == 1) ? "" : "s") .
  207.                 ")\n";
  208.         }
  209.  
  210.         $this->answer = array();
  211.         for ($ctr = 0; $ctr < $this->header->ancount; $ctr++) {
  212.             list($rrobj, $offset) = $this->parse_rr($data, $offset);
  213.  
  214.             if (is_null($rrobj)) {
  215.                 return(NULL);
  216.             }
  217.             array_push($this->answer, $rrobj);
  218.             if ($this->debug) {
  219.                 $rrobj->display();
  220.             }
  221.         }
  222.  
  223.         /*
  224.          *  Print and parse the UPDATE or AUTHORITY section of the packet
  225.          */
  226.         if ($this->debug) {
  227.             echo "\n";
  228.             $section = ($this->header->opcode == "UPDATE") ? "UPDATE" : "AUTHORITY";
  229.             echo ";; $section SECTION (" .
  230.                 $this->header->nscount . " record" .
  231.                 (($this->header->nscount == 1) ? "" : "s") .
  232.                 ")\n";
  233.         }
  234.  
  235.         $this->authority = array();
  236.         for ($ctr = 0; $ctr < $this->header->nscount; $ctr++) {
  237.             list($rrobj, $offset) = $this->parse_rr($data, $offset);
  238.  
  239.             if (is_null($rrobj)) {
  240.                 return(NULL);
  241.             }
  242.             array_push($this->authority, $rrobj);
  243.             if ($this->debug) {
  244.                 $rrobj->display();
  245.             }
  246.         }
  247.  
  248.         /*
  249.          *  Print and parse the ADDITIONAL section of the packet
  250.          */
  251.         if ($this->debug) {
  252.             echo "\n";
  253.             echo ";; ADDITIONAL SECTION (" .
  254.                 $this->header->arcount . " record" .
  255.                 (($this->header->arcount == 1) ? "" : "s") .
  256.                 ")\n";
  257.         }
  258.  
  259.         $this->additional = array();
  260.         for ($ctr = 0; $ctr < $this->header->arcount; $ctr++) {
  261.             list($rrobj, $offset) = $this->parse_rr($data, $offset);
  262.  
  263.             if (is_null($rrobj)) {
  264.                 return(NULL);
  265.             }
  266.             array_push($this->additional, $rrobj);
  267.             if ($this->debug) {
  268.                 $rrobj->display();
  269.             }
  270.         }
  271.  
  272.         return(TRUE);
  273.     }
  274.  
  275.     /* }}} */
  276.     /* Net_DNS_Packet::data() {{{*/
  277.     /**
  278.      * Build a packet from a Packet object hierarchy
  279.      *
  280.      * Builds a valid DNS packet suitable for sending to a DNS server or
  281.      * resolver client containing all of the data in the packet hierarchy.
  282.      *
  283.      * @return string A binary string containing a DNS Packet
  284.      */
  285.     function data()
  286.     {
  287.         $data = $this->header->data();
  288.  
  289.         for ($ctr = 0; $ctr < $this->header->qdcount; $ctr++) {
  290.             $data .= $this->question[$ctr]->data($this, strlen($data));
  291.         }
  292.  
  293.         for ($ctr = 0; $ctr < $this->header->ancount; $ctr++) {
  294.             $data .= $this->answer[$ctr]->data($this, strlen($data));
  295.         }
  296.  
  297.         for ($ctr = 0; $ctr < $this->header->nscount; $ctr++) {
  298.             $data .= $this->authority[$ctr]->data($this, strlen($data));
  299.         }
  300.  
  301.         for ($ctr = 0; $ctr < $this->header->arcount; $ctr++) {
  302.             $data .= $this->additional[$ctr]->data($this, strlen($data));
  303.         }
  304.  
  305.         return($data);
  306.     }
  307.  
  308.     /*}}}*/
  309.     /* Net_DNS_Packet::dn_comp($name, $offset) {{{*/
  310.     /**
  311.      * DNS packet compression method
  312.      *
  313.      * Returns a domain name compressed for a particular packet object, to
  314.      * be stored beginning at the given offset within the packet data.  The
  315.      * name will be added to a running list of compressed domain names for
  316.      * future use.
  317.      * 
  318.      * @param string    $name       The name of the label to compress
  319.      * @param integer   $offset     The location offset in the packet to where
  320.      *                              the label will be stored.
  321.      * @return string   $compname   A binary string containing the compressed
  322.      *                              label.
  323.      * @see Net_DNS_Packet::dn_expand()
  324.      */
  325.     function dn_comp($name, $offset)
  326.     {
  327.         $names = explode(".", $name);
  328.         $compname = "";
  329.         while (count($names)) {
  330.             $dname = join(".", $names);
  331.             if (isset($this->compnames[$dname])) {
  332.                 $compname .= pack("n", 0xc000 | $this->compnames[$dname]);
  333.                 break;
  334.             }
  335.  
  336.             $this->compnames[$dname] = $offset;
  337.             $first = array_shift($names);
  338.             $length = strlen($first);
  339.             $compname .= pack("Ca*", $length, $first);
  340.             $offset += $length + 1;
  341.         }
  342.         if (! count($names)) {
  343.             $compname .= pack("C", 0);
  344.         }
  345.         return($compname);
  346.     }
  347.  
  348.     /*}}}*/
  349.     /* Net_DNS_Packet::dn_expand($packet, $offset) {{{ */
  350.     /**
  351.      * DNS packet decompression method
  352.      *
  353.      * Expands the domain name stored at a particular location in a DNS
  354.      * packet.  The first argument is a variable containing  the packet
  355.      * data.  The second argument is the offset within the  packet where
  356.      * the (possibly) compressed domain name is stored.
  357.      * 
  358.      * @param   string  $packet The packet data
  359.      * @param   integer $offset The location offset in the packet of the
  360.      *                          label to decompress.
  361.      * @return  array   Returns a list of type array($name, $offset) where
  362.      *                  $name is the name of the label which was decompressed
  363.      *                  and $offset is the offset of the next field in the
  364.      *                  packet.  Returns array(NULL, NULL) on error
  365.      */
  366.     function dn_expand($packet, $offset)
  367.     {
  368.         $packetlen = strlen($packet);
  369.         $int16sz = 2;
  370.         $name = "";
  371.         while (1) {
  372.             if ($packetlen < ($offset + 1)) {
  373.                 return(array(NULL, NULL));
  374.             }
  375.  
  376.             $a = unpack("@$offset/Cchar", $packet);
  377.             $len = $a["char"];
  378.  
  379.             if ($len == 0) {
  380.                 $offset++;
  381.                 break;
  382.             } else if (($len & 0xc0) == 0xc0) {
  383.                 if ($packetlen < ($offset + $int16sz)) {
  384.                     return(array(NULL, NULL));
  385.                 }
  386.                 $ptr = unpack("@$offset/ni", $packet);
  387.                 $ptr = $ptr["i"];
  388.                 $ptr = $ptr & 0x3fff;
  389.                 $name2 = Net_DNS_Packet::dn_expand($packet, $ptr);
  390.  
  391.                 if (is_null($name2[0])) {
  392.                     return(array(NULL, NULL));
  393.                 }
  394.                 $name .= $name2[0];
  395.                 $offset += $int16sz;
  396.                 break;
  397.             } else {
  398.                 $offset++;
  399.  
  400.                 if ($packetlen < ($offset + $len)) {
  401.                     return(array(NULL, NULL));
  402.                 }
  403.  
  404.                 $elem = substr($packet, $offset, $len);
  405.                 $name .= $elem . ".";
  406.                 $offset += $len;
  407.             }
  408.         }
  409.         $name = ereg_replace("\.$", "", $name);
  410.         return(array($name, $offset));
  411.     }
  412.  
  413.     /*}}}*/
  414.     /* Net_DNS_Packet::parse_question($data, $offset) {{{ */
  415.     /**
  416.      * Parses the question section of a packet
  417.      *
  418.      * Examines a DNS packet at the specified offset and parses the data
  419.      * of the QUESTION section.
  420.      *
  421.      * @param   string  $data   The packet data returned from the server
  422.      * @param   integer $offset The location offset of the start of the
  423.      *                          question section.
  424.      * @return  array   An array of type array($q, $offset) where $q
  425.      *                  is a Net_DNS_Question object and $offset is the
  426.      *                  location of the next section of the packet which
  427.      *                  needs to be parsed.
  428.      */
  429.     function parse_question($data, $offset)
  430.     {
  431.         list($qname, $offset) = $this->dn_expand($data, $offset);
  432.         if (is_null($qname)) {
  433.             return(array(NULL, NULL));
  434.         }
  435.  
  436.         if (strlen($data) < ($offset + 2 * 2)) {
  437.             return(array(NULL, NULL));
  438.         }
  439.  
  440.         $q = unpack("@$offset/n2int", $data);
  441.         $qtype = $q["int1"];
  442.         $qclass = $q["int2"];
  443.         $offset += 2 * 2;
  444.  
  445.         $qtype = Net_DNS::typesbyval($qtype);
  446.         $qclass = Net_DNS::classesbyval($qclass);
  447.  
  448.         $q = new Net_DNS_Question($qname, $qtype, $qclass);
  449.         return(array($q, $offset));
  450.     }
  451.  
  452.     /*}}}*/
  453.     /* Net_DNS_Packet::parse_rr($data, $offset) {{{ */
  454.     /**
  455.      * Parses a resource record section of a packet
  456.      *
  457.      * Examines a DNS packet at the specified offset and parses the data
  458.      * of a section which contains RRs (ANSWER, AUTHORITY, ADDITIONAL).
  459.      *
  460.      * @param string    $data   The packet data returned from the server
  461.      * @param integer   $offset The location offset of the start of the resource
  462.      *                          record section.
  463.      * @return  array   An array of type array($rr, $offset) where $rr
  464.      *                  is a Net_DNS_RR object and $offset is the
  465.      *                  location of the next section of the packet which
  466.      *                  needs to be parsed.
  467.      */
  468.     function parse_rr($data, $offset)
  469.     {
  470.         list($name, $offset) = $this->dn_expand($data, $offset);
  471.         if (! strlen($name)) {
  472.             return(array(NULL, NULL));
  473.         }
  474.  
  475.         if (strlen($data) < ($offset + 10)) {
  476.             return(array(NULL, NULL));
  477.         }
  478.  
  479.         $a = unpack("@$offset/n2tc/Nttl/nrdlength", $data);
  480.         $type = $a["tc1"];
  481.         $class = $a["tc2"];
  482.         $ttl = $a["ttl"];
  483.         $rdlength = $a["rdlength"];
  484.  
  485.         $type = Net_DNS::typesbyval($type);
  486.         $class = Net_DNS::classesbyval($class);
  487.  
  488.         $offset += 10;
  489.         if (strlen($data) < ($offset + $rdlength)) {
  490.             return(array(NULL, NULL));
  491.         }
  492.  
  493.         $rrobj = new Net_DNS_RR(array($name,
  494.                     $type,
  495.                     $class,
  496.                     $ttl,
  497.                     $rdlength,
  498.                     $data,
  499.                     $offset));
  500.  
  501.         if (is_null($rrobj)) {
  502.             return(array(NULL, NULL));
  503.         }
  504.  
  505.         $offset += $rdlength;
  506.  
  507.         return(array($rrobj, $offset));
  508.     }
  509.  
  510.     /* }}} */
  511.     /* Net_DNS_Packet::display() {{{ */
  512.     /**
  513.      * Prints out the packet in a human readable formatted string
  514.      */
  515.     function display()
  516.     {
  517.         echo $this->string();
  518.     }
  519.  
  520.     /*}}}*/
  521.     /* Net_DNS_Packet::string() {{{ */
  522.     /**
  523.      * Builds a human readable formatted string representing a packet
  524.      */ 
  525.     function string()
  526.     {
  527.         $retval = "";
  528.         if ($this->answerfrom) {
  529.             $retval .= ";; Answer received from " . $this->answerfrom . "(" .
  530.                 $this->answersize . " bytes)\n;;\n";
  531.         }
  532.  
  533.         $retval .= ";; HEADER SECTION\n";
  534.         $retval .= $this->header->string();
  535.         $retval .= "\n";
  536.  
  537.         $section = ($this->header->opcode == "UPDATE") ? "ZONE" : "QUESTION";
  538.         $retval .= ";; $section SECTION (" . $this->header->qdcount     .
  539.             " record" . ($this->header->qdcount == 1 ? "" : "s") .
  540.             ")\n";
  541.  
  542.         foreach ($this->question as $qr) {
  543.             $retval .= ";; " . $qr->string() . "\n";
  544.         }
  545.  
  546.         $section = ($this->header->opcode == "UPDATE") ? "PREREQUISITE" : "ANSWER";
  547.         $retval .= "\n;; $section SECTION (" . $this->header->ancount     .
  548.             " record" . ($this->header->ancount == 1 ? "" : "s") .
  549.             ")\n";
  550.  
  551.         if (is_array($this->answer)) {
  552.             foreach ($this->answer as $ans) {
  553.                 $retval .= ";; " . $ans->string() . "\n";
  554.             }
  555.         }
  556.  
  557.         $section = ($this->header->opcode == "UPDATE") ? "UPDATE" : "AUTHORITY";
  558.         $retval .= "\n;; $section SECTION (" . $this->header->nscount     .
  559.             " record" . ($this->header->nscount == 1 ? "" : "s") .
  560.             ")\n";
  561.  
  562.         if (is_array($this->authority)) {
  563.             foreach ($this->authority as $auth) {
  564.                 $retval .= ";; " . $auth->string() . "\n";
  565.             }
  566.         }
  567.  
  568.         $retval .= "\n;; ADDITIONAL SECTION (" . $this->header->arcount     .
  569.             " record" . ($this->header->arcount == 1 ? "" : "s") .
  570.             ")\n";
  571.  
  572.         if (is_array($this->additional)) {
  573.             foreach ($this->additional as $addl) {
  574.                 $retval .= ";; " . $addl->string() . "\n";
  575.             }
  576.         }
  577.  
  578.         $retval .= "\n\n";
  579.         return($retval);
  580.     }
  581.  
  582.     /*}}}*/
  583. }
  584. /* }}} */
  585. /* VIM settings {{{
  586.  * Local variables:
  587.  * tab-width: 4
  588.  * c-basic-offset: 4
  589.  * soft-stop-width: 4
  590.  * c indent on
  591.  * End:
  592.  * vim600: sw=4 ts=4 sts=4 cindent fdm=marker et
  593.  * vim<600: sw=4 ts=4
  594.  * }}} */
  595. ?>
  596.